#region Using directives

using System;
using System.Collections.Generic;
using System.Text;
using Weazel.Gezel.Simulator.Reference.Profiling;

#endregion

namespace Weazel.Gezel.Simulator.Reference
{
    /// <summary>
    /// Implements the runtime functionality
    /// of a gezel $display directive
    /// </summary>
    class DisplayDirective : IDirective
    {
        /// <summary>
        /// If no other base has been specified
        /// integers are printed as hexidecimals
        /// </summary>
        private int defaultBase = 16;
        public int Base
        {
            get
            {
                return defaultBase;
            }
            set
            {
                defaultBase = value;
            }
        }

        private readonly Symid id, sfg;

        /// <summary>
        /// The datapath which contains this $display directive
        /// </summary>
        private readonly Datapath datapath;

        /// <summary>
        /// List of items to display: strings and signals and
        /// $cycle, $sfg directives
        /// </summary>
        private readonly List<AbstractDisplay> displayList;

        /// <summary>
        /// Holds the radix used to print each of the items
        /// in the display list. 
        /// 
        /// TODO: radixes could be handled more elegantly
        /// </summary>
        private readonly List<int> currentBase;

        /// <summary>
        /// Instantiates a new $display directive attached to the
        /// signalflowgraph with the specified id and contained in
        /// the specified datapath
        /// </summary>
        /// <param name="id">id of the $display directive</param>
        /// <param name="sfg">id of the containing signalflowgraph</param>
        /// <param name="datapath">the containing datapath</param>
        public DisplayDirective(Symid id, Symid sfg, Datapath datapath)
        {
            this.id = id;
            this.sfg = sfg;
            this.datapath = datapath;

            displayList = new List<AbstractDisplay>();
            currentBase = new List<int>();
        }

        /// <summary>
        /// Add a string to be displayed
        /// </summary>
        /// <param name="str"></param>
        public void AddString(string str)
        {
            displayList.Add(new StringDisplay(str));
            currentBase.Add(defaultBase);
        }

        /// <summary>
        /// Add a signal to be displayed with the
        /// defautl base
        /// </summary>
        /// <param name="signal">signal to display</param>
        public void AddVariable(AbstractSignal signal)
        {
            displayList.Add(new SignalDisplay(signal));
            currentBase.Add(defaultBase);
        }

        /// <summary>
        /// Add a $cycle directive
        /// </summary>
        public void AddCycle()
        {
            displayList.Add(new CycleDisplay());
            currentBase.Add(defaultBase);
        }

        /// <summary>
        /// Add a $toggle directive
        /// </summary>
        internal void AddToggle(Profiler counter)
        {
            displayList.Add(new ToggleDisplay(counter));
            currentBase.Add(defaultBase);
        }

        /// <summary>
        /// Execute the $display directive and output
        /// the contents to the buffer
        /// </summary>
        /// <param name="g">generation</param>
        /// <param name="buffer">output buffer</param>
        public void Execute(long g, StringBuilder buffer)
        {
            for (int i = 0; i < displayList.Count; i++)
            {
                AbstractDisplay display = displayList[i];
                display.SetBase(currentBase[i]);
                display.Show(g, buffer);
            }

            buffer.AppendLine(); // output newline
        }

        /// <summary>
        /// Determine if this $display is eglible for execution
        /// </summary>
        /// <returns>true iff the $display should be executed</returns>
        public bool CanExecute()
        {
            if (datapath.IsSignalFlowGraphActive(sfg))
                return true;
            else
                return false;
        }
    }
}
